/**
 *  TinyPasswordManager
 *  Copyright (C) 2008 Gross David <gdavid.devel@gmail.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "firefoxpasswordmanager.h"

#undef UNICODE
#define _CRT_SECURE_NO_DEPRECATE 1
#define _CRT_NONSTDC_NO_DEPRECATE 1
#define _CRT_SECURE_NO_WARNINGS 1
#define _WIN32_WINNT 0x0502

#include <windows.h>
#include <shlobj.h>

FirefoxPasswordManager::FirefoxPasswordManager()
 : m_pathLibraries("C:\\Program Files\\Mozilla Firefox\\") {
}

FirefoxPasswordManager::FirefoxPasswordManager(const std::string &pathLibraries)
 : m_pathLibraries(pathLibraries) {
}

void FirefoxPasswordManager::init() 
throw(ExNSSInternalError, ExLibraryNotFound) {
    try {
        loadLibraries();
    }
    catch(ExLibraryNotFound &ex) {
        throw ex;
    }
	
	char tmp[MAX_PATH];
	SHGetSpecialFolderPath(0, tmp, CSIDL_APPDATA, FALSE);	
    std::string pathFirefoxData = std::string(tmp) + "\\Mozilla\\Firefox";
    
	GetPrivateProfileString("Profile0", "Path", "", tmp, MAX_PATH, std::string(pathFirefoxData + "\\profiles.ini").c_str());
	std::string pathProfile = pathFirefoxData + "\\" + tmp;
	m_pathSignons = pathProfile + "\\signons.sqlite";
    
    char copyPathProfile[MAX_PATH];
    strcpy(copyPathProfile, pathProfile.c_str());
    
	if(m_NSS_Init(copyPathProfile) != SECSuccess)
		throw ExNSSInternalError(__FUNCTION__, "NSS_Init");

	if((m_keySlot = m_PK11_GetInternalKeySlot()) == NULL)
		throw ExNSSInternalError(__FUNCTION__, "PK11_GetInternalKeySlot");
}

bool FirefoxPasswordManager::checkUserPassword(const std::string &password) {
	bool result = m_PK11_CheckUserPassword(m_keySlot, password.c_str()) == SECSuccess;
    
    return result;
}

void FirefoxPasswordManager::getCredentials(std::vector<Identifier *> &idents)
throw(ExNSSInternalError, ExSQLiteError) {
	sqlite3 *db;
    
	if(m_PK11_Authenticate(m_keySlot, TRUE, NULL) != SECSuccess)
		throw ExNSSInternalError(__FUNCTION__, "PK11_Authenticate");        
	    
	if(m_sqlite3_open(m_pathSignons.c_str(), &db) != SQLITE_OK)
		throw ExSQLiteError(__FUNCTION__, m_sqlite3_errmsg(db));

	std::string query("SELECT * FROM moz_logins");
	sqlite3_stmt *stmt;
    
	if(m_sqlite3_prepare_v2(db, query.c_str(), query.size(), &stmt, NULL) != SQLITE_OK)
		throw ExSQLiteError(__FUNCTION__, m_sqlite3_errmsg(db));
	
    std::string::size_type loc;
    
	while(m_sqlite3_step(stmt) == SQLITE_ROW) {
		std::string cipheredLogin = reinterpret_cast<const char *>(m_sqlite3_column_text(stmt, 6));
		std::string cipheredPassword = reinterpret_cast<const char *>(m_sqlite3_column_text(stmt, 7));

		std::string plaintextLogin = decipher(cipheredLogin);
		std::string plaintextPassword = decipher(cipheredPassword);

        std::string hostname = reinterpret_cast<const char *>(m_sqlite3_column_text(stmt, 1));
        
        if((loc = hostname.find("://")) != std::string::npos)
            hostname = hostname.substr(loc + 3);
        if((loc = hostname.find("www.")) != std::string::npos)
            hostname = hostname.substr(loc + 4);
            
        while((loc = plaintextLogin.find("\n")) != std::string::npos)
            plaintextLogin.erase(loc);
        while((loc = plaintextPassword.find("\n")) != std::string::npos)
            plaintextPassword.erase(loc);
        while((loc = hostname.find("\n")) != std::string::npos)
            hostname.erase(loc);
        
        idents.push_back(new Identifier(plaintextLogin, plaintextPassword, hostname));
	}

	m_sqlite3_finalize(stmt);	
	m_sqlite3_close(db);
}

void FirefoxPasswordManager::terminate() {
	m_PK11_FreeSlot(m_keySlot);
	m_NSS_Shutdown();
}
    
void FirefoxPasswordManager::loadLibraries()
throw(ExLibraryNotFound) {
	HMODULE moduleSQLite, moduleNSS;
    std::string str;
    
    str = m_pathLibraries + "\\mozcrt19.dll";
	if(!LoadLibrary(str.c_str()))
		throw ExLibraryNotFound(__FUNCTION__, "mozcrt19.dll");

    str = m_pathLibraries + "\\sqlite3.dll";
	if(!(moduleSQLite = LoadLibrary(str.c_str())))
		throw ExLibraryNotFound(__FUNCTION__, "sqlite3.dll");

    str = m_pathLibraries + "\\nspr4.dll";
	if(!LoadLibrary(str.c_str()))
		throw ExLibraryNotFound(__FUNCTION__, "nspr4.dll");

    str = m_pathLibraries + "\\plc4.dll";
	if(!LoadLibrary(str.c_str()))
		throw ExLibraryNotFound(__FUNCTION__, "plc4.dll");

    str = m_pathLibraries + "\\plds4.dll";
	if(!LoadLibrary(str.c_str()))
		throw ExLibraryNotFound(__FUNCTION__, "plds4.dll");

    str = m_pathLibraries + "\\nssutil3.dll";
	if(!LoadLibrary(str.c_str()))
		throw ExLibraryNotFound(__FUNCTION__, "nssutil3.dll");

    str = m_pathLibraries + "\\softokn3.dll";
	if(!LoadLibrary(str.c_str()))
		throw ExLibraryNotFound(__FUNCTION__, "softokn3.dll");

    str = m_pathLibraries + "\\nss3.dll";
	if(!(moduleNSS = LoadLibrary(str.c_str())))
		throw ExLibraryNotFound(__FUNCTION__, "nss3.dll");
    
    m_sqlite3_open = (SQLite3_open)GetProcAddress(moduleSQLite, "sqlite3_open");
    m_sqlite3_errmsg = (SQLite3_errmsg)GetProcAddress(moduleSQLite, "sqlite3_errmsg");
    m_sqlite3_prepare_v2 = (SQLite3_prepare_v2)GetProcAddress(moduleSQLite, "sqlite3_prepare_v2");
    m_sqlite3_step = (SQLite3_step)GetProcAddress(moduleSQLite, "sqlite3_step");
    m_sqlite3_finalize = (SQLite3_finalize)GetProcAddress(moduleSQLite, "sqlite3_finalize");
    m_sqlite3_close = (SQLite3_close)GetProcAddress(moduleSQLite, "sqlite3_close");
    m_sqlite3_column_text = (SQLite3_column_text)GetProcAddress(moduleSQLite, "sqlite3_column_text");

	m_NSS_Init = (NSSInit)GetProcAddress(moduleNSS, "NSS_Init");
	m_PK11_GetInternalKeySlot = (PK11GetInternalKeySlot)GetProcAddress(moduleNSS, "PK11_GetInternalKeySlot");
	m_PK11_Authenticate = (PK11Authenticate)GetProcAddress(moduleNSS, "PK11_Authenticate");
	m_PK11SDR_Decrypt = (PK11SDRDecrypt)GetProcAddress(moduleNSS, "PK11SDR_Decrypt");
	m_NSSBase64_DecodeBuffer = (NSSBase64DecodeBuffer)GetProcAddress(moduleNSS, "NSSBase64_DecodeBuffer");
	m_PK11_CheckUserPassword = (PK11CheckUserPassword)GetProcAddress(moduleNSS, "PK11_CheckUserPassword");
	m_NSS_Shutdown = (NSSShutdown)GetProcAddress(moduleNSS, "NSS_Shutdown");
	m_PK11_FreeSlot = (PK11FreeSlot)GetProcAddress(moduleNSS, "PK11_FreeSlot");
}

std::string FirefoxPasswordManager::decipher(const std::string &cipheredBuffer) {
	SECItem request;
	SECItem reply;
	unsigned int len = cipheredBuffer.size() + 1;
    
    char *tmp = (char *)malloc(len);
    strcpy(tmp, cipheredBuffer.c_str());

	request.data = reinterpret_cast<unsigned char *>(tmp);
	request.len = len;
	reply.data = 0;
	reply.len = 0;

	m_NSSBase64_DecodeBuffer(NULL, &request, cipheredBuffer.c_str(), len);
	m_PK11SDR_Decrypt(&request, &reply, NULL);

	reply.data[reply.len] = 0;
    free(tmp);

	return std::string(reinterpret_cast<char *>(reply.data));
}

    